using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.WowAddonStudio.FrameXml.Services;

namespace Microsoft.WowAddonStudio.FrameXml.Design.Services.NameCreationService
{
	/// <summary>
	/// Provides a service that can generate unique names for FrameXmlControl objects.
	/// </summary>
	[Guid("D632A067-8248-458d-8C0F-B73DDED46FF0")]
	public class FrameXmlNameCreationService : IFrameXmlNameCreationService
	{
		private readonly DataTypeMapper dataTypeMapper = new DataTypeMapper();

		/// <summary>
		/// Initializes a new instance of the <see cref="FrameXmlNameCreationService"/> class.
		/// </summary>
		/// <param name="loaderHost">The loader host.</param>
		public FrameXmlNameCreationService(IDesignerLoaderHost loaderHost)
		{
			if (loaderHost == null)
				throw new ArgumentNullException("loaderHost");

			this.LoaderHost = loaderHost;
		}

		/// <summary>
		/// Gets or sets the loader host.
		/// </summary>
		/// <value>The loader host.</value>
		protected IDesignerLoaderHost LoaderHost { get; private set; }

		#region INameCreationService Members

		/// <summary>
		/// Creates a new name that is unique to all components in the specified container.
		/// </summary>
		/// <param name="container">The container where the new object is added.</param>
		/// <param name="dataType">The data type of the object that receives the name.</param>
		/// <returns>A unique name for the data type.</returns>
		public string CreateName(IContainer container, Type dataType)
		{
			if (dataType == null)
				throw new ArgumentNullException("dataType");

			string result;
			try
			{
				var context = new UniqueNumberGeneratorContext(new SimpleUniqueNumberGenerator(container));
				string dataTypeName = dataTypeMapper[dataType].Name;

				result = String.Format(CultureInfo.InvariantCulture, "{0}{1}",
				                       dataTypeName, context.Generator.GenerateUniqueNumber(dataTypeName));
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex);
				result = String.Format(CultureInfo.InvariantCulture, "{0}_{1:B}", dataType.Name, Guid.NewGuid());
			}

			return result;
		}

		/// <summary>
		/// Creates a new name that is unique to all components in the specified container.
		/// </summary>
		/// <param name="container">The container where the new object is added.</param>
		/// <param name="prefixName">Name of the prefix.</param>
		/// <returns>A unique name for the data type.</returns>
		public string CreateName(IContainer container, string prefixName)
		{
			if (prefixName == null)
				throw new ArgumentNullException("prefixName");

			string result;
			try
			{
				var context = new UniqueNumberGeneratorContext(new SimpleUniqueNumberGenerator(container));
				result = String.Format(CultureInfo.InvariantCulture, "{0}{1}",
									   prefixName, context.Generator.GenerateUniqueNumber(prefixName));
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex);
				result = String.Format(CultureInfo.InvariantCulture, "{0}_{1:B}", prefixName, Guid.NewGuid());
			}

			return result;
		}

		/// <summary>
		/// Gets a value indicating whether the specified name is valid.
		/// </summary>
		/// <param name="control">The control.</param>
		/// <param name="layoutFrameName">Name of the layout frame.</param>
		/// <returns>
		/// true if the name is valid; otherwise, false.
		/// </returns>
		public bool HasValidName(Control control, string layoutFrameName)
		{
			if (control == null)
				throw new ArgumentNullException("control");

			bool result = true;
			if (!string.IsNullOrEmpty(control.Name))
			{
				var ctrl = FindControl(LoaderHost.RootComponent as Control, layoutFrameName);
				if (ctrl != null && ctrl != control)
				{
					result = false;
				}
			}
			return result;
		}

        /// <summary>
        /// Determines whether [has valid name] [the specified component].
        /// </summary>
        /// <param name="component">The component.</param>
        /// <param name="componentName">Name of the component.</param>
        /// <returns>
        /// 	<c>true</c> if [has valid name] [the specified component]; otherwise, <c>false</c>.
        /// </returns>
        public bool HasValidName(Component component, string componentName)
        {
            if (component == null)
                throw new ArgumentNullException("component");

            bool result = true;
            if (!String.IsNullOrEmpty(component.Site.Name))
            {
                var cmpnnt = FindComponent(LoaderHost.RootComponent as Control, componentName);
                if (cmpnnt != null && cmpnnt != component)
                {
                    result = false;
                }
            }
            return result;
        }

		/// <summary>
		/// Gets a value indicating whether the specified name is valid.
		/// </summary>
		/// <param name="name">The name to validate.</param>
		/// <returns>
		/// true if the name is valid; otherwise, false.
		/// </returns>
		public bool IsValidName(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name");
			
			if (name.Length == 0)
				return false;

			return IsNameAvailable(name);
		}

		/// <summary>
		/// Gets a value indicating whether the specified name is valid.
		/// </summary>
		/// <param name="name">The name to validate.</param>
		public void ValidateName(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name");

			if (name.Length == 0)
				throw new ArgumentException("Empty identifiers are not valid.", "name");

			//TODO: add more checking...
		}

		#endregion

		#region Helper Functions

		/// <summary>
		/// Determines whether [is name available] [the specified control name].
		/// </summary>
		/// <param name="controlName">Name of the control.</param>
		/// <returns>
		/// 	<c>true</c> if [is name available] [the specified control name]; otherwise, <c>false</c>.
		/// </returns>
		private bool IsNameAvailable(string controlName)
		{
			bool result = true;
			if (!string.IsNullOrEmpty(controlName))
			{
				if (FindControl(LoaderHost.RootComponent as Control, controlName) != null)
				{
					result = false;
				}
			}
			return result;
		}

		/// <summary>
		/// Finds the control.
		/// </summary>
		/// <param name="component">The component.</param>
		/// <param name="controlName">Name of the control.</param>
		/// <returns></returns>
		public Control FindControl(Control component, string controlName)
		{
			Control control = null;
			if (component != null)
			{
				if (component.Name == controlName)
				{
					control = component;
				}
				else
				{
					if (component.Controls.Count > 0)
					{
						foreach (Control ctrl in component.Controls)
						{
							control = FindControl(ctrl, controlName);
							if (control != null)
								return control;
						}
					}
				}
			}
			return control;
		}

        /// <summary>
        /// Finds the component.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <param name="componentName">Name of the component.</param>
        /// <returns></returns>
        public Component FindComponent(Component component, string componentName)
        {
            Component result = null;

            var components = component.Container.Components.OfType<Component>();
            foreach (var cmp in components)
            {
                if(cmp.Site.Name == componentName)
                {
                    return cmp;
                }
            }

            return result;
        }

		#endregion

	}
}